package cn.larry.tools.weixin;

import cn.larry.tools.random.RandomPasswordTool;
import cn.larry.tools.rsa.RSATool;
import cn.larry.tools.string.StringTool;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

/**
 * Larry
 * 2022/1/4 13:44
 *
 * @Version 1.0
 */
public class WeixinPayTool {

    private String mchId;
    private String privateKey;
    private String mchSerialNo;
    private String apiV3Key;
    private CloseableHttpClient httpClient;
    private ScheduledUpdateCertificatesVerifier verifier;

    @Deprecated
    public WeixinPayTool(String mchId,
                         String privateKey,
                         String mchSerialNo,
                         String apiV3Key) throws Exception {
        this.mchId = mchId;
        this.privateKey = privateKey;
        this.mchSerialNo = mchSerialNo;
        this.apiV3Key = apiV3Key;

        // 加载商户私钥（privateKey：私钥字符串）
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));

        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3密钥）
        verifier = new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
                apiV3Key.getBytes("utf-8"));

        // 初始化httpClient
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();

    }

    /**
     * 小程序调起支付API
     * （参考：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml）
     *
     * @param appId
     * @param bos
     * @return
     * @throws Exception
     */
    @Deprecated
    public String creatJsapi(String appId, ByteArrayOutputStream bos) throws Exception {

        String prepayId = getPrepayId(bos);
        if (StringTool.isBlank(prepayId))
            throw new RuntimeException("JSAPI下单失败");
        Object object = JSONObject.parseObject(prepayId).get("prepay_id");
        if (object == null)
            throw new RuntimeException("JSAPI下单失败");

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("timeStamp", System.currentTimeMillis() / 1000);
        jsonObject.put("nonceStr", RandomPasswordTool.randomGenerate2(18));
        jsonObject.put("package", "prepay_id=" + object.toString());
        jsonObject.put("signType", "RSA");
        jsonObject.put("appId", appId);

        StringBuilder sb = new StringBuilder(appId + "\n");
        sb.append(jsonObject.get("timeStamp") + "\n")
                .append(jsonObject.get("nonceStr") + "\n")
                .append(jsonObject.get("package") + "\n");
        jsonObject.put("paySign", getPaySign(sb.toString()));
        return jsonObject.toString();

    }

    /**
     * 签名：程序支付字段'paySign'
     *
     * @param str
     * @return
     * @throws Exception
     */
    @Deprecated
    public String getPaySign(String str) throws Exception {
        return RSATool.sign(str, privateKey);
    }

    /**
     * 对回调通知的解密
     *
     * @param associated_data
     * @param nonce
     * @param ciphertext
     * @return
     * @throws GeneralSecurityException
     */
    @Deprecated
    public JSONObject decryptByNotify(String associated_data, String nonce, String ciphertext) throws GeneralSecurityException {
        AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));

        //解密
        return JSONObject.parseObject(aesUtil.decryptToString(
                associated_data.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext));
    }

    /**
     * JSAPI下单
     * （参考：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml）
     *
     * @param bos
     * @return
     * @throws Exception
     */
    @Deprecated
    public String getPrepayId(ByteArrayOutputStream bos) throws Exception {

        HttpPost httpPost = null;
        CloseableHttpResponse response = null;
        try {
//            httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
            httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");

            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                if (response != null)
                    response.close();
            } catch (Exception e) {
            }
            try {
                if (httpPost != null)
                    httpPost.clone();
            } catch (Exception e) {
            }
        }
    }

    /**
     * 添加分账接收方
     * （参考：https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_8.shtml）
     *
     * @param bos
     * @return
     * @throws Exception
     */
    public String receiversAdd(ByteArrayOutputStream bos) throws Exception {
        return postV3Api(bos, "https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add");
    }

    /**
     * 请求分账
     * （参考：https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_1.shtml）
     *
     * @param bos
     * @return
     * @throws Exception
     */
    public String profitsharingOrders(ByteArrayOutputStream bos) throws Exception {
        return postV3Api(bos, "https://api.mch.weixin.qq.com/v3/profitsharing/orders");
    }

    /**
     * POST请求V3版本接口
     *
     * @param bos 请求参数
     * @param url 请求地址
     * @return
     * @throws Exception
     */
    public String postV3Api(ByteArrayOutputStream bos, String url) throws Exception {

        HttpPost httpPost = null;
        CloseableHttpResponse response = null;
        try {
            httpPost = new HttpPost(url);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");

            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                if (response != null)
                    response.close();
            } catch (Exception e) {
            }
            try {
                if (httpPost != null)
                    httpPost.clone();
            } catch (Exception e) {
            }
        }
    }

    /**
     * 交易订单查询
     * （参考：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_2.shtml）
     *
     * @param out_trade_no
     * @param mchid
     * @return
     * @throws Exception
     */
    @Deprecated
    public String query(String out_trade_no, String mchid) throws Exception {

        HttpGet httpGet = null;
        CloseableHttpResponse response = null;
        try {
            String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + out_trade_no
                    + "?mchid=" + mchid;

            httpGet = new HttpGet(url);
            httpGet.addHeader("Accept", "application/json");
            httpGet.addHeader("Content-type", "application/json; charset=utf-8");

            response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                if (response != null)
                    response.close();
            } catch (Exception e) {
            }
            try {
                if (httpGet != null)
                    httpGet.clone();
            } catch (Exception e) {
            }
        }
    }

    /**
     * 关闭订单
     * （参考：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_3.shtml）
     *
     * @param out_trade_no
     * @param mchid
     * @return （返回"HTTP/1.1 204 No Content"是成功的）
     * @throws Exception
     */
    @Deprecated
    public String close(String out_trade_no, String mchid) throws Exception {

        HttpPost http = null;
        CloseableHttpResponse response = null;
        try {
            String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + out_trade_no + "/close";

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", mchid);
            objectMapper.writeValue(bos, rootNode);

            http = new HttpPost(url);
            http.addHeader("Accept", "application/json");
            http.addHeader("Content-type", "application/json; charset=utf-8");

            http.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            response = httpClient.execute(http);
            return response.getStatusLine().toString();
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                if (response != null)
                    response.close();
            } catch (Exception e) {
            }
            try {
                if (http != null)
                    http.clone();
            } catch (Exception e) {
            }
        }
    }

    /**
     * 关流（一定要记得哟）
     */
    public void close() {
        if (httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
            }
        }
        if (verifier != null) {
            try {
                verifier.stopScheduledUpdate();
            } catch (Exception e) {
            }
        }
    }

}
